/*
 *  policy - Part of the Policy Server Application
 *  Copyright (C) 2009 Liam Lowey
 *  
 *      http://policyserver.turnitin.co.uk/
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  you may not use this file except in compliance with the License. 
 *  You may obtain a copy of the License at 
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0 
 *  
 *  Unless required by applicable law or agreed to in writing, software 
 *  distributed under the License is distributed on an "AS IS" BASIS, 
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 *  See the License for the specific language governing permissions and 
 *  limitations under the License. 
 */

using System;
using System.Collections.Generic;
using System.Threading;

namespace policyserver
{
  class CommandQueue
  {
    private readonly LinkedList<ICommand> _lowPriorityQueue;
    private readonly LinkedList<ICommand> _highPriorityQueue;
    private readonly object _thisLock;

    public CommandQueue()
    {
      _thisLock = new object();      
      _lowPriorityQueue = new LinkedList<ICommand>();
      _highPriorityQueue = new LinkedList<ICommand>();
    }


    public void push( ICommand cmd )
    {
      lock( _thisLock )
      {
        doPushExtra();
        if( _lowPriorityQueue.Count == 0 && _highPriorityQueue.Count == 0 )
        {
          Monitor.PulseAll( _thisLock );
        }
        _lowPriorityQueue.AddLast( cmd );
      }
    }


    public void pushUrgent( ICommand cmd )
    {
      lock( _thisLock )
      {
        doPushExtra();
        if( areEmptyUnsafe )
        {
          Monitor.PulseAll( _thisLock );
        }
        _highPriorityQueue.AddLast( cmd );
      }
    }


    public bool hasCommand
    {
      get
      {
        lock( _thisLock )
        {
          return (!areEmptyUnsafe);
        }
      }
    }


    public ICommand pop( TimeSpan time )
    {
      ICommand ret = null;
      
      lock( _thisLock )
      {
        if( areEmptyUnsafe ) 
        {
          if( !Monitor.Wait( _thisLock, time ) )
          {
            doPopExtra();
            return null;
          }
        }

        if( _highPriorityQueue.Count > 0 )
        {
          ret = _highPriorityQueue.First.Value;
          _highPriorityQueue.RemoveFirst();
        }
        else if( _lowPriorityQueue.Count > 0 )
        {
          ret = _lowPriorityQueue.First.Value;
          _lowPriorityQueue.RemoveFirst();
        }

        doPopExtra();
      }

      return ret;
    }


    protected virtual void doPushExtra() { }
    protected virtual void doPopExtra() { }


    private bool areEmptyUnsafe
    {
      get
      {
        return (_lowPriorityQueue.Count == 0 && _highPriorityQueue.Count == 0);
      }
    }
  }
}
